package com.naiterui.ehp.bs.recommend.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.naiterui.ehp.bp.bo.medication.MedicationDefaultUsageBO;
import com.naiterui.ehp.bp.bo.recommend.param.RebuyNoticeParamBO;
import com.naiterui.ehp.bp.bo.recommend.param.RebuyRecomParamBO;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.beans.BeanUtil;
import com.naiterui.ehp.bp.utils.lang.StringUtil;
import com.naiterui.ehp.bs.recommend.bean.MedicationRecomDetail;
import com.naiterui.ehp.bs.recommend.exception.ExceptionCodes;
import com.naiterui.ehp.bs.recommend.remote.IRecommendRemoter;
import com.naiterui.ehp.bs.recommend.repository.mysql.IMedicationRecomDetailRepository;
import com.naiterui.ehp.bs.recommend.service.IRecomDetailService;
import com.naiterui.ehp.bs.recommend.util.RecomBizUtil;
import com.naiterui.ehp.bs.recommend.vo.MedicationRecomDetailVO;
import com.naiterui.ehp.bs.recommend.vo.RecomDrugCycleVO;

@Service
public class RecomDetailServiceImpl implements IRecomDetailService {

    private static Logger LOGGER = LoggerFactory.getLogger(RecomDetailServiceImpl.class);

    @Autowired
    private IMedicationRecomDetailRepository medicationRecomDetailRepository;

    @Autowired
    private IRecommendRemoter remoter;

    /**
     * 根据推荐ID查询推荐详情列表
     * MedicationRecomServiceImpl.getDetailsByRecomId()
     *
     * @param recommendId
     *
     * @return
     *
     * @Author guoyongxiang
     * @Date 2016年6月16日
     * @since 1.0.0
     */
    @Override
    public List<MedicationRecomDetailVO> getDetailsByRecomId(Long recommendId) {
        List<MedicationRecomDetail> recomDetailList = this.medicationRecomDetailRepository.findByRecommendId(recommendId);
        List<MedicationRecomDetailVO> recomDetailVOList = new ArrayList<>();
        BeanUtil.copyList(recomDetailList, recomDetailVOList, MedicationRecomDetailVO.class);
        return recomDetailVOList;
    }


    @Override
    public List<MedicationRecomDetailVO> getDetailsByRecomIds(Set<Long> recomIdSet) {

        List<MedicationRecomDetail> recomDetailList = this.medicationRecomDetailRepository.findByRecommendIdIn(recomIdSet);
        List<MedicationRecomDetailVO> recomDetailVOList = new ArrayList<>();
        BeanUtil.copyList(recomDetailList, recomDetailVOList, MedicationRecomDetailVO.class);
        recomDetailVOList.forEach(e -> e.setDrugCycleDays(RecomBizUtil.getDrugCycle(e.getDrugCycle(), e.getDrugCycleUnit())));
        return recomDetailVOList;

    }


    @Override
    public RecomDrugCycleVO getLongestRecommend(RebuyNoticeParamBO rebuyNoticeParamBO) throws BusinessException {
        if (rebuyNoticeParamBO == null || CollectionUtils.isEmpty(rebuyNoticeParamBO.getRecomBuyList())) {
            LOGGER.error("获取最长用药周期推荐信息参数错误, paramVO: {}", rebuyNoticeParamBO);
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        }
        // 获取参数中的推荐id集合
        List<RebuyRecomParamBO> recomBuyList = rebuyNoticeParamBO.getRecomBuyList();
        Set<Long> recomIdSet = new HashSet<>();
        Map<String, Double> drugCycleMap = new HashMap<>();// 用药周期map,key: recommendid_skuid,
        // value:用药周期天数
        for (RebuyRecomParamBO recomInfoBO : recomBuyList) {
            Long recommendId = recomInfoBO.getRecommenId();
            recomIdSet.add(recommendId);
            List<Long> skuIdList = recomInfoBO.getSkuIdList();
            for (Long skuId : skuIdList) {
                drugCycleMap.put(recommendId + "_" + skuId, 0d);
            }
        }

        // 获取购买处方的推荐详情
        List<MedicationRecomDetail> recomDetailList = this.medicationRecomDetailRepository.findByRecommendIdIn(recomIdSet);
        if (CollectionUtils.isNotEmpty(recomDetailList)) {
            Set<Long> noDrugCycleSkuids = new HashSet<Long>();
            List<MedicationRecomDetail> noCycleRecomDetailList = new ArrayList<>();
            for (MedicationRecomDetail recomDetail : recomDetailList) {
                String tempKey = recomDetail.getRecommendId() + "_" + recomDetail.getSkuId();
                if (drugCycleMap.get(tempKey) != null) {
                    Double drugCylceDays = RecomBizUtil.getDrugCycle(recomDetail.getDrugCycle(), recomDetail.getDrugCycleUnit());
                    if (drugCylceDays == null) {
                        noDrugCycleSkuids.add(recomDetail.getSkuId());
                        noCycleRecomDetailList.add(recomDetail);
                    } else {
                        drugCycleMap.put(tempKey, drugCylceDays);
                        if (LOGGER.isDebugEnabled()) {
                            LOGGER.debug("医生填写周期：recommendId: {}, skuId: {},drugCycle : {},drugCycleUnit: {},drugCylceDays: {}",
                                    recomDetail.getRecommendId(), recomDetail.getSkuId(), recomDetail.getDrugCycle(),
                                    recomDetail.getDrugCycleUnit(), drugCylceDays);
                        }
                    }
                }
            }

            // 计算无医生填写的用药周期的药品服用周期
            if (noDrugCycleSkuids.size() > 0) {
                Map<Long, MedicationDefaultUsageBO> usageMap = this.getMedicalDefaultUsageMap(noDrugCycleSkuids);
                for (MedicationRecomDetail recomDetail : noCycleRecomDetailList) {
                    Double drugCylceDays = this.calculateDrugCycle(recomDetail, usageMap.get(recomDetail.getSkuId()));
                    String tempKey = recomDetail.getRecommendId() + "_" + recomDetail.getSkuId();
                    drugCycleMap.put(tempKey, drugCylceDays);
                }
            }

            // 获取多个处方中用药周期最长的推荐信息
            RecomDrugCycleVO cycleVO = this.getLongestRecommendMapKey(drugCycleMap);
            return cycleVO;
        }
        return null;
    }

    /**
     * 计算多个处方中用药周期最长的推荐
     * RecomDetailServiceImpl.getLongestRecommendCycle()
     *
     * @param drugCycleMap
     *
     * @return
     *
     * @Date 2018年6月28日
     * @since 2.19
     */
    private RecomDrugCycleVO getLongestRecommendMapKey(Map<String, Double> drugCycleMap) {
        if (drugCycleMap == null) {
            return null;
        }
        Iterator<Entry<String, Double>> it = drugCycleMap.entrySet().iterator();
        Double maxCycle = 0d;
        Long maxCycleRecommnedId = 0L;
        Long maxCycleSkuId = 0L;
        while (it.hasNext()) {
            Entry<String, Double> drugCycleEntry = it.next();
            String mapKey = drugCycleEntry.getKey();
            String recommendIdStr = mapKey.split("_")[0];
            String skuIdStr = mapKey.split("_")[1];
            Double cycle = drugCycleEntry.getValue();
            if (cycle != null && cycle > maxCycle) {
                maxCycle = cycle;
                maxCycleRecommnedId = Long.valueOf(recommendIdStr);
                maxCycleSkuId = Long.valueOf(skuIdStr);
            }
        }
        if (maxCycle != 0d) {
            RecomDrugCycleVO cycleVO = new RecomDrugCycleVO();
            cycleVO.setRecommendId(maxCycleRecommnedId);
            cycleVO.setSkuId(maxCycleSkuId);
            Integer cycleI = (int) Math.ceil(maxCycle);
            cycleVO.setDrugCycle(cycleI);
            return cycleVO;
        }
        return null;
    }

    /**
     * 获取药品默认用法用量map
     * RecomDetailServiceImpl.getMedicalDefaultUsageMap()
     *
     * @param noDrugCycleSkuids
     *
     * @return
     *
     * @throws BusinessException
     * @Date 2018年6月28日
     * @since 2.19
     */
    private Map<Long, MedicationDefaultUsageBO> getMedicalDefaultUsageMap(Set<Long> noDrugCycleSkuids) throws BusinessException {
        if (noDrugCycleSkuids == null || noDrugCycleSkuids.size() == 0) {
            return null;
        }
        String skuIds = StringUtil.convertCollection(noDrugCycleSkuids);
        List<MedicationDefaultUsageBO> defaultUsageList = this.remoter.getDefaultUsage(skuIds);
        Map<Long, MedicationDefaultUsageBO> defaultUsageMap = new HashMap<Long, MedicationDefaultUsageBO>();

        if (CollectionUtils.isNotEmpty(defaultUsageList)) {
            for (MedicationDefaultUsageBO usageBO : defaultUsageList) {
                defaultUsageMap.put(usageBO.getSkuId(), usageBO);
            }
        }
        return defaultUsageMap;
    }

    /**
     * 根据默认用法用量和医生填写的用法用量计算用药周期
     * RecomDetailServiceImpl.calculateDrugCycle()
     *
     * @param recomDetail
     * @param defaultUsage
     *
     * @return
     *
     * @Date 2018年6月28日
     * @since 2.19
     */

    @Override
    public Double calculateDrugCycle(MedicationRecomDetail recomDetail, MedicationDefaultUsageBO defaultUsage) {
        if (recomDetail == null || defaultUsage == null) {
            return null;
        }

        /** 最小规格包装数（如：一盒有多少片 ） */
        Integer packagSpec = defaultUsage.getPackagSpec();
        // 用量周期(每[2]日1次,有0.5的情况)
        Double dosageCycle = recomDetail.getDosageCycle();
        // 用量周期单位(每2[日]1次)
        String dosageCycleUnit = recomDetail.getDosageCycleUnit();
        // 用量次数(每2日[1]次,有若干次的情况)
        String dosageCount = recomDetail.getDosageCount();
        // 每次用量(每次[3]片,有适量的情况)
        String eachDosageCount = recomDetail.getEachDosageCount();

        Integer quantity = recomDetail.getQuantity();
        Long recommendId = recomDetail.getRecommendId();
        Long skuId = recomDetail.getSkuId();

        Double durgCycle = null;
        if (packagSpec == null || dosageCycle == null || StringUtils.isEmpty(dosageCycleUnit) || StringUtils.isEmpty(dosageCount)
                || StringUtils.isEmpty(eachDosageCount) || quantity == null) {
            LOGGER.warn(
                    "计算周期参数不全，忽略计算。packagSpec: {}，dosageCycle：{},dosageCycleUnit: {},dosageCount: {},eachDosageCount: {},quantity: {},recommendId: " +
                            "{},skuId: {}",
                    packagSpec, dosageCycle, dosageCycleUnit, dosageCount, eachDosageCount, quantity, recommendId, skuId);
            return null;
        }

        // 判断医生填写的单位与药品默认单位是否一致，不一致则不参与计算
        String defaultEachDoseUnit = defaultUsage.getEachDoseUnit();
        String recomEachDoseUnit = recomDetail.getEachDoseUnit();
        if (StringUtils.isEmpty(defaultEachDoseUnit) || StringUtils.isEmpty(recomEachDoseUnit)
                || !recomEachDoseUnit.trim().equals(defaultEachDoseUnit.trim())) {
            LOGGER.warn("计算周期参数药品服用单位与默认单位不一致，忽略计算。defaultEachDoseUnit: {}，recomEachDoseUnit：{},recommendId: {},skuId: {}", defaultEachDoseUnit,
                    recomEachDoseUnit, recommendId, skuId);
            return null;
        }

        int dosageCountI = 0;
        try {
            dosageCountI = Integer.parseInt(dosageCount);
        } catch (NumberFormatException e) {
            LOGGER.warn("用量次数为非数字，忽略计算。dosageCount: {},recommendId: {},skuId: {}", dosageCount, recommendId, skuId, e);
            return null;
        }
        double eachDosageCountD = 0;
        try {
            eachDosageCountD = Double.parseDouble(eachDosageCount);
        } catch (NumberFormatException e) {
            LOGGER.warn("每次用量为非数字，忽略计算。eachDosageCount: {},recommendId: {},skuId: {}", eachDosageCount, recommendId, skuId, e);
            return null;
        }
        // 单位周期计算数 = 单位周期（换算成小时）/24，每日为24小时，24/24=1
        Double unitCycle = RecomBizUtil.getDrugCycle(dosageCycle, dosageCycleUnit);
        if (unitCycle == null) {
            LOGGER.warn("单位周期计算数结果为空，忽略计算。dosageCycle: {},dosageCycleUnit: {},recommendId: {},skuId: {}", dosageCycle, dosageCycleUnit, recommendId,
                    skuId);
            return null;
        }
        // 一盒药可服用的天数 = 最小单位包装规格 ÷ 每次服用数量 ÷ 次数周期 * 单位周期计算数
        durgCycle = packagSpec / eachDosageCountD / dosageCountI * unitCycle;
        durgCycle = durgCycle * quantity;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(
                    "无医生填写周期，计算用药周期：recommendId: {}, skuId: {},dosageCycle : {},dosageCycleUnit: {},unitCycle: {},packagSpec: {}, eachDosageCount: " +
                            "{},dosageCount:{},unitCycle:{}, quantity: {},计算结果: {}",
                    recommendId, skuId, dosageCycle, dosageCycleUnit, unitCycle + "天", packagSpec,
                    eachDosageCount, dosageCount, unitCycle, quantity, durgCycle);
        }
        return durgCycle;
    }

}
